package com.wzh.core.consumer;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.wzh.core.api.LoadBalance;
import com.wzh.core.api.Router;
import com.wzh.core.api.RpcContent;
import com.wzh.core.api.entity.RpcException;
import com.wzh.core.api.entity.RpcRequest;
import com.wzh.core.api.entity.RpcResponse;
import com.wzh.core.utils.RpcMethodUtils;
import com.wzh.core.utils.TypeUtils;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @description: rpc消费者属性代理类
 * @author: wangzouhuax
 */
public class RpcConsumerInvocationHandlerHandler implements InvocationHandler {

    private static final Logger log = LoggerFactory.getLogger(RpcConsumerInvocationHandlerHandler.class);

    private final String serviceName;

    private RpcContent rpcContent;

    private List<String> providers;

    private final OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectionPool(new ConnectionPool(16, 60, TimeUnit.SECONDS))
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .build();


    public RpcConsumerInvocationHandlerHandler(String serviceName, RpcContent rpcContent, List<String> providers) {
        this.serviceName = serviceName;
        this.rpcContent = rpcContent;
        this.providers = providers;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setServiceName(serviceName);
        rpcRequest.setMethodSignature(RpcMethodUtils.getMethodSignature(method));
        rpcRequest.setArgs(args);
        // 检查方法是否为RpcMethod，不是则直接返回
        if (RpcMethodUtils.checkRpcMethod(method)) {
            return null;
        }

        RpcResponse<Object> rpcResponse = getRpcResponse(rpcRequest, method.getReturnType(), method.getGenericReturnType());
        if (Boolean.TRUE.equals(rpcResponse.getStatus())) {
            return rpcResponse.getData();
        } else {
            return null;
        }
    }

    /**
     * 通过Http请求，获取RpcResponse
     */
    private <T> RpcResponse<T> getRpcResponse(RpcRequest rpcRequest, Class<?> returnType, Type genericReturnType) {

        try {
            Gson gson = new Gson();
            String requestData = gson.toJson(rpcRequest);

            // 负载均衡，获取url

            String url = rpcContent.getLoadBalance().choose(rpcContent.getRouter().getRoutes(providers)).toString();
            Response response = okHttpClient.newCall(new Request.Builder()
                    .url(url)
                    .post(RequestBody.create(requestData, MediaType.get("application/json;charset=utf-8")))
                    .build()).execute();
            // 输出流
            assert response.body() != null;
            String responseData = response.body().string();
            // 因为返回的类型是带泛型的 RpcResponse<T> 所以需要
            // TypeToken.getParameterized(RpcResponse.class, returnType)
            // 创建一个新的 TypeToken 实例，这个实例表示了参数化类型（即带有泛型的类型）
            // RpcResponse.class 表示基础类型是RpcResponse。
            // returnType 是一个Class对象，代表了RpcResponse类型中泛型的具体类型参数。
            // 基本数据类型不支持泛型，所以需要使用getRawType()方法获取原始类型。
            // 结合这两个参数，这个方法能够生成如 RpcResponse<String>、RpcResponse<Integer> 这样的参数化类型信息。
            Class<?> realType = TypeUtils.cast(returnType);
            if (realType != null) {
                // 处响应结果是 集合的泛型， 即 RpcResponse<List<?>>
                if (List.class.isAssignableFrom(realType) && genericReturnType instanceof ParameterizedType parameterizedType) {
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    // 处理List<actualTypeArguments>
                    TypeToken<?> listTypeToken = TypeToken.getParameterized(List.class, actualTypeArguments);
                    // 处理RpcResponse<List<listTypeToken>>
                    TypeToken<?> parameterized = TypeToken.getParameterized(RpcResponse.class, listTypeToken.getType());
                    return gson.fromJson(responseData, parameterized.getType());
                }
                if (Map.class.isAssignableFrom(realType) && genericReturnType instanceof ParameterizedType parameterizedType) {
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    TypeToken<?> mapTypeToken = TypeToken.getParameterized(Map.class, actualTypeArguments);
                    TypeToken<?> parameterized = TypeToken.getParameterized(RpcResponse.class, mapTypeToken.getType());
                    return gson.fromJson(responseData, parameterized.getType());
                }
            }

            // 这样的类型信息在使用如Gson这样的库进行泛型对象的序列化与反序列化时非常关键，因为它允许这些库正确地理解并处理泛型类型，从而保证类型安全地转换数据。
            TypeToken<?> parameterized = TypeToken.getParameterized(RpcResponse.class, realType);
            // 再用Gson转化成 RpcResponse 对象
            return gson.fromJson(responseData, parameterized.getType());
        } catch (Exception e) {
            log.error("调用服务提供者异常", e);
            throw new RpcException("调用服务提供者异常");
        }
    }
}
